/**
 * 使用Mybatis的XML解析来进行SQL构建的执行器
 */

package crud.template;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;


import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.scripting.xmltags.XMLScriptBuilder;
import org.apache.ibatis.session.Configuration;
import crud.domain.SQLExecuteParam;


public class SQLTemplate {
    private static final String EMPTY_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n"
            + "<!DOCTYPE configuration\r\n"
            + " PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\"\r\n"
            + " \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\r\n"
            + "<configuration>\r\n"
            + "</configuration>";
    private static final Configuration parseConfig;

    static {
        InputStream inputStream = new ByteArrayInputStream(EMPTY_XML.getBytes(StandardCharsets.UTF_8));
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(inputStream, null, null);
        parseConfig = xmlConfigBuilder.parse();
    }

    static public SQLExecuteParam buildTemplate(String sqlTemplate, Map<String, Object> sqlParams) {

        String script = String.format("<script>%s</script>", sqlTemplate);
        XPathParser parser = new XPathParser(script, false, new Properties(), new XMLMapperEntityResolver());
        XMLScriptBuilder builder = new XMLScriptBuilder(parseConfig, parser.evalNode("/script"), null);
        SqlSource source = builder.parseScriptNode();

        List<Object> paramList = new ArrayList<>();
        BoundSql boundSql = source.getBoundSql(sqlParams);

        String buildSql = boundSql.getSql();

        List<ParameterMapping> pmList = boundSql.getParameterMappings();
        for (ParameterMapping pm : pmList) {
            if (pm.getMode() != ParameterMode.OUT) {
                String paramName = pm.getProperty();
                if (paramName.contains("param.")) {
                    paramName = paramName.replace("param.", "");
                    Object paramValue = sqlParams.get(paramName);
                    paramList.add(paramValue);
                } else {
                    Object paramValue = sqlParams.get(paramName);
                    paramList.add(paramValue);
                }
            }
        }

        SQLExecuteParam res = new SQLExecuteParam();
        res.setSql(buildSql);
        res.setParam(paramList);

        return res;
    }

}